package top.hudk.design.single;

/**
 * 简单懒汉式单例模式
 * 适用于单线程环境
 * 缺点：
 * 1、容易受到反射攻击
 * 2、不适用于多线程
 *
 * @author hudk
 * @date 2020/3/1 14:43
 */
public class LazySingleton {
    private LazySingleton instance = null;
    /**
     * 私有构造方法，防止外部调用来实例化
     */
    private LazySingleton() {
    }
    public LazySingleton getInstance() {
        if (instance == null) {
            instance = new LazySingleton();
            return instance;
        }
        return instance;
    }
}

/**
 * 懒汉式单例模式
 * 简单加锁版
 * 适用于多线程
 * 缺点：
 * 1、容易受到反射攻击
 * 2、性能问题：每次示例化时都需要加锁，锁对性能影响很大
 */
class LazySingleton2 {
    private LazySingleton2 instance2 = null;
    /**
     * 私有构造方法，防止外部调用来实例化
     */
    private LazySingleton2() {
    }
    public synchronized LazySingleton2 getInstance2() {
        if (instance2 == null) {
            instance2 = new LazySingleton2();
            return instance2;
        }
        return instance2;
    }
}

/**
 * 懒汉式单例模式
 * 局部加锁版
 * 适用于多线程。性能问题解决
 * 缺点：
 * 1、具有反射漏洞
 * 2、容易出现指令重排导致的空指针异常问题：
 */
class LazySingleton3 {
    private LazySingleton3 instance3 = null;
    /**
     * 私有构造方法，防止外部调用来实例化
     */
    private LazySingleton3() {
    }
    public LazySingleton3 getInstance3() {
        if (instance3 == null) {
            synchronized (LazySingleton3.class) {
                if (instance3 == null) {
                    instance3 = new LazySingleton3();
                    return instance3;
                    /**
                     * 此处：new LazySingleton();
                     * 虚拟机，会分为三步：
                     * 1、分配内存
                     * 2、初始化实例
                     * 3、为引用赋值
                     * 由于，指令重排的存在，第2、3步可能顺序颠倒。
                     * 如果顺序颠倒的话，就会出现：
                     * 当一个线程执行到为引用赋值，但是实际的实例还未初始化时，
                     * 另一个线程正好执行到上面第一次判断是否为空的位置，那么该线程就会得到实例不为空的结果，
                     * 但是实例现在其实是没有初始化的，只是对应的引用已经有了实例在内存中的地址值了而已。
                     * 这样，第二个线程有可能带着一个空的“实例”去执行其他的操作，所以有可能会出现不可预测的空指针异常问题。
                     */
                }
            }
        }
        return instance3;
    }
}

/**
 * 懒汉式单例模式
 * 通过局部加锁版，且禁止指令重排
 * 适用于多线程。性能问题和指令重排问题被解决
 * 缺点：
 * 1、具有反射漏洞
 */
class LazySingleton4 {
    /**
     * volatile关键词使属性new时不会被指令重排
     */
    private volatile LazySingleton4 instance4 = null;
    /**
     * 私有构造方法，防止外部调用来实例化
     */
    private LazySingleton4() {
    }
    public LazySingleton4 getInstance4() {
        if (instance4 == null) {
            synchronized (LazySingleton4.class) {
                if (instance4 == null) {
                    instance4 = new LazySingleton4();
                    return instance4;
                    /**
                     * 此处：new LazySingleton();
                     * 虚拟机，会分为三步：
                     * 1、分配内存
                     * 2、初始化实例
                     * 3、为引用赋值
                     * 由于，全局成员定义时，禁止了指令重排，第2、3步的顺序不会颠倒。
                     * 就不会出现不可预测的空指针异常
                     */
                }
            }
        }
        return instance4;
    }
}

/**
 * 懒汉式单例模式
 * 通过局部加锁版，且禁止指令重排，加上防反射检查。
 * 适用于多线程。性能问题、指令重排、和反射问题被解决
 */
class LazySingleton5 {
    /**
     * volatile关键词使属性new时不会被指令重排
     */
    private volatile LazySingleton5 instance5 = null;
    /**
     * 私有构造方法，防止外部调用来实例化
     */
    private LazySingleton5() {
        /**
         * 这里的判断，是为了避免反射攻击造成的多实例出现。
         */
        if(instance5 != null){
            throw new RuntimeException("本对象是单例模式，系统内不允许多出现个实例");
        }
    }
    public LazySingleton5 getInstance5() {
        if (instance5 == null) {
            synchronized (LazySingleton5.class) {
                if (instance5 == null) {
                    instance5 = new LazySingleton5();
                    return instance5;
                    /**
                     * 此处：new LazySingleton();
                     * 虚拟机，会分为三步：
                     * 1、分配内存
                     * 2、初始化实例
                     * 3、为引用赋值
                     * 由于，全局成员定义时，禁止了指令重排，第2、3步的顺序不会颠倒。
                     * 就不会出现不可预测的空指针异常
                     */
                }
            }
        }
        return instance5;
    }
}
